Newer
Older
taehui / taehui-fe / src / app / [language] / forum / components / TextView.tsx
@Taehui Taehui on 3 Jun 2 KB v1.0.0
import scss from "@/app/[language]/forum/components/TextView.module.scss";
import useGetHit from "@/app/[language]/forum/query/useGetHit";
import AvatarDrawing from "@/components/AvatarDrawing";
import { getHitTexts, tag } from "@/utilities/Utility";
import { sanitize } from "isomorphic-dompurify";
import { useTranslations } from "next-intl";
import { useEffect, useMemo, useState } from "react";
import Col from "react-bootstrap/Col";
import Row from "react-bootstrap/Row";
import Stack from "react-bootstrap/Stack";
import { getDatetime } from "taehui-lib/date";

export default function TextView({
  title,
  text,
  avatarID,
  avatarName,
  date,
  hitCount,
}: {
  title: string;
  text: string;
  avatarID: string;
  avatarName: string;
  date?: string;
  hitCount?: number;
}) {
  const hitTexts = useMemo(() => getHitTexts(text), [text]);
  const [textHTMLContents, setTextHTMLContents] = useState("");

  const t = useTranslations();

  const { data: hit, isFetched: isHitLoaded } = useGetHit(hitTexts);

  useEffect(() => {
    setTextHTMLContents(
      sanitize(tag(t, text, {}, scss.essay), {
        ADD_ATTR: ["target"],
      }),
    );
  }, [t, text]);

  useEffect(() => {
    if (isHitLoaded) {
      setTextHTMLContents(
        sanitize(
          tag(
            t,
            text,
            Object.fromEntries(hitTexts.map((hitText, i) => [hitText, hit[i]])),
            scss.essay,
          ),
          {
            ADD_ATTR: ["target"],
          },
        ),
      );
    }
  }, [hit, hitTexts, isHitLoaded, t, text]);

  return (
    <>
      <Row>
        <Col xs="auto">
          <AvatarDrawing avatarID={avatarID} />
        </Col>
        <Col>
          <Stack gap={2}>
            <span>{avatarName}</span>
            <span>{title}</span>
          </Stack>
        </Col>
        <Col xs="auto">
          <Stack gap={2} className="text-end">
            <span>{getDatetime(date)}</span>
            {typeof hitCount === "number" && (
              <span>{t("hitCount", { hitCount })}</span>
            )}
          </Stack>
        </Col>
      </Row>
      <hr />
      <span
        style={{ whiteSpace: "break-spaces" }}
        dangerouslySetInnerHTML={{
          __html: textHTMLContents,
        }}
      />
    </>
  );
}